#pragma once

#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <cstring>

#include <string>
#include <unordered_map>
#include <memory>
#include <functional>

#include "socket.hpp"
#include "nocopy.hpp"
#include "epoller.hpp"
#include "thread_pool.hpp"
#include "log.hpp"
extern Log log;

class Connection;
class TcpServer;

using func_t = std::function<void(std::shared_ptr<Connection>)>;

class Connection
{
public:
    Connection(int socket_fd, TcpServer* tcp_server_ptr)
        : _socket_fd(socket_fd)
        , _tcp_server_ptr(tcp_server_ptr)
    {}

    void set_callback(func_t recv, func_t send, func_t except)
    {
        _recv_callback = recv;
        _send_callback = send;
        _except_callback = except;
    }
    void set_ip_port(const std::string& ip, uint16_t port)
    {
        _ip = ip;
        _port = port;
    }
public:
    const std::string& ip()
    {
        return _ip;
    }
    const uint16_t port()
    {
        return _port;
    }
    int socket_fd()
    {
        return _socket_fd;
    }
    std::string& Inbuffer()
    {
        return _inbuffer;
    }
    std::string& Outbuffer()
    {
        return _outbuffer;
    }

private:
    int _socket_fd = -1;
    std::string _inbuffer;  // 存储输入二进制流
    std::string _outbuffer; // 存储输出二进制流
public:
    func_t _recv_callback;  // 读取回调
    func_t _send_callback;  // 发送回调
    func_t _except_callback;// 异常回调

    TcpServer* _tcp_server_ptr; // 回调指针
private:
    std::string _ip;
    uint16_t _port = 0;
};

void set_no_block(int fd)
{
    int f = fcntl(fd, F_GETFL);
    if (f < 0)
    {
        log(Fatal, "fcntl error, errno: %d, err string: %s", errno, strerror(errno));
        exit(-1);
    }
    else
        fcntl(fd, F_SETFL, f | O_NONBLOCK);
}

class TcpServer : public nocopy
{
public:
    TcpServer(func_t on_message = nullptr)
        : _epoller_ptr(new Epoller)
        , _thread_pool_ptr(new ThreadPool<>)
        , _on_message(on_message)
    {}

    void init()
    {
        // 服务器运行状态
#ifdef __TEST__DEBUG__
        signal(SIGCHLD, SIG_IGN);
#else
        log.change_method(Class);
        daemon(1, 0);
#endif
        _thread_pool_ptr->start();
    }

    void start()
    {
        _quit = false;
        while (!_quit)
        {
            // 从epoll中获取事件,并进行分发
            int nums = _epoller_ptr->wait(revents, Epoller::Size);
            Dispatcher(revents, nums);
        }
    }
    // connections管理函数
    // 判断fd是否存在于connection
    bool connection_safe(int fd)
    {
        return (_connections.find(fd) != _connections.end());
    }
public:
    // 添加fd进connections
    void add_connection(int socket_fd, uint32_t event, func_t recv, func_t send, func_t except, \
                        const std::string& ip = "0.0.0.0", uint16_t port = 0)
    {
        // 将fd添加至epoll中
        _epoller_ptr->control(socket_fd, Epoller::FD_ADD, event);
        // 设置Connection
        std::shared_ptr<Connection> ptr = std::make_shared<Connection>(socket_fd, this);
        ptr->set_callback(recv, send, except);
        ptr->set_ip_port(ip, port);
        // 添加进hashmap中
        _connections[socket_fd] = ptr;
    }
    // 从connections删除fd
    void del_connection(int socket_fd)
    {
        // epoll中删除fd
        _epoller_ptr->control(socket_fd, Epoller::FD_DEL);
        // hashmap中删除
        _connections.erase(socket_fd);
        // 关闭fd
        close(socket_fd);
    }

    void enable_event(int fd, bool read, bool write, bool edge_triggered)
    {
        uint32_t events = 0;
        events |= (read ? Epoller::EVENT_IN : 0) | (write ? Epoller::EVENT_OUT : 0) | (edge_triggered ? Epoller::EVENT_ET : 0);;
        _epoller_ptr->control(fd, Epoller::FD_MOD, events);
    }
private:
    void Dispatcher(struct epoll_event* revents, int nums)
    {
        // 遍历epoll返回的事件集合,并根据其响应事件调用回调
        for (size_t i = 0; i < nums; ++i)
        {
#ifdef __TEST__DEBUG__
            log(Info, "fd %d has event", revents[i].data.fd);
#endif
            uint32_t event = revents[i].events;
            uint32_t fd = revents[i].data.fd;
            // 若fd出现事件异常,将其统一转化为读写问题
            if (event & Epoller::EVENT_ERR || event & Epoller::EVENT_HUP)
                event |= Epoller::EVENT_IN | Epoller::EVENT_OUT;
            
            // 检测fd是否存在于connections中
            if (event & Epoller::EVENT_IN && connection_safe(fd) && _connections[fd]->_recv_callback)
            {
                // _connections[fd]->_recv_callback(_connections[fd]);
                _thread_pool_ptr->push(std::bind(_connections[fd]->_recv_callback, _connections[fd]));
            }
            if (event & Epoller::EVENT_OUT && connection_safe(fd) && _connections[fd]->_send_callback)
            {
                _thread_pool_ptr->push(std::bind(_connections[fd]->_send_callback, _connections[fd]));
                // _connections[fd]->_send_callback(_connections[fd]);
            }
        }
    }
private:
    bool _quit = false;
    // epoll响应事件
    struct epoll_event revents[Epoller::Size];
    // epoll管理
    std::shared_ptr<Epoller> _epoller_ptr;
    // 链接fd管理
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;
    std::shared_ptr<ThreadPool<>> _thread_pool_ptr;
public:
    func_t _on_message;
};

class link_socket
{
public:
    void Recv(std::shared_ptr<Connection> connection)
    {
        static const size_t read_size = 128;
        while (true)
        {
            char buffer[read_size] = { 0 };
            ssize_t n = recv(connection->socket_fd(), buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = '\0';
                connection->Inbuffer() += buffer;
            }
            else if (n == 0)
            {
                log(Info, "client exit, fd %d close, clientip: %s, port: %d", connection->socket_fd(), connection->ip().c_str(), connection->port());
                connection->_except_callback(connection);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK)   // 数据读完
                    break;
                else if (errno == EINTR)    // 遇到中断
                    continue;
                else
                {
                    log(Warning, "Recv error, fd: %d, clientip: %s, port: %d", connection->socket_fd(), connection->ip().c_str(), connection->port());\
                    connection->_except_callback(connection);
                    return;
                }
            }
        }
        if (connection->_tcp_server_ptr->_on_message)
            connection->_tcp_server_ptr->_on_message(connection);
    }
    void Send(std::shared_ptr<Connection> connection)
    {
        while (true)
        {
            ssize_t n = send(connection->socket_fd(), connection->Outbuffer().c_str(), connection->Outbuffer().size(), 0);
            if (n > 0)
            {
                connection->Outbuffer().erase(0, n);
                // 按需进行写设置
                if (connection->Outbuffer().empty())
                    break;
            }
            else if (n == 0)
                break;
            else
            {
                if (errno == EWOULDBLOCK)   // 发送缓冲区空间不够
                    break;
                else if (errno == EINTR)    // 遇到中断
                    continue;
                else
                {
                    log(Warning, "Send error, fd: %d, clientip: %s, port: %d", connection->socket_fd(), connection->ip().c_str(), connection->port());\
                    connection->_except_callback(connection);
                    return;
                }
            }
        }
        // 一次没有将数据发送完毕,才开启写事件关心
        if (!connection->Outbuffer().empty())
            connection->_tcp_server_ptr->enable_event(connection->socket_fd(), true, true, true);
        else
            connection->_tcp_server_ptr->enable_event(connection->socket_fd(), true, false, true);
    }
    void Except(std::shared_ptr<Connection> connection)
    {
        log(Warning, "except handler fd: %d, clientip: %s, port: %d", connection->socket_fd(), connection->ip().c_str(), connection->port());
        // if (connection->_tcp_server_ptr->connection_safe(connection->socket_fd()))   // 关闭该文件描述符
            connection->_tcp_server_ptr->del_connection(connection->socket_fd());
    }
};

class Listener
{
    const static size_t default_port = 8081;
public:
    Listener(int port = default_port)
        : _port(port)
        , _listen_socket_ptr(new Socket)
    {}
    void init()
    {
        _listen_socket_ptr->Create(SOCK_STREAM);
        set_no_block(_listen_socket_ptr->socket_fd());
        _listen_socket_ptr->Bind(_port);
        _listen_socket_ptr->Listen();
    }
    int socket_fd()
    {
        return _listen_socket_ptr->socket_fd();
    }
    void Accept(std::shared_ptr<Connection> connection)
    {
        // listen_socket为非阻塞,边沿触发,故需要一次性读完
        while (true)
        {
            std::string client_ip;
            uint16_t client_port = 0;
            int fd = _listen_socket_ptr->Accept(client_ip, client_port);
            if (fd > 0)
            {
                // 设置的为ET触发,故应先将fd设为非阻塞
                set_no_block(fd);
                link_socket link;
                connection->_tcp_server_ptr->add_connection(fd, Epoller::EVENT_IN | Epoller::EVENT_ET, \
                    std::bind(&link_socket::Recv, &link, std::placeholders::_1), \
                    std::bind(&link_socket::Send, &link, std::placeholders::_1), \
                    std::bind(&link_socket::Except, &link, std::placeholders::_1), \
                    client_ip, client_port);
            }
            else
            {
                if (errno == EWOULDBLOCK)   // 数据读完
                    break;
                else if (errno == EINTR)    // 遇到中断
                    continue;
                else
                {
                    log(Warning, "fd: %d, clientip: %s, port: %d, Accpet error", connection->socket_fd(), connection->ip().c_str(), connection->port());\
                    connection->_except_callback(connection);
                    break;
                }
            }
        }
    }
private:
    uint16_t _port = 0;
    std::shared_ptr<Socket> _listen_socket_ptr;
};